package ru.ganyaev.workstation_vm_manager;

import java.nio.file.Path;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ru.ganyaev.external_process.BasicExternalProcess;
import ru.ganyaev.external_process.Command;
import ru.ganyaev.external_process.ExecuteExternallProcessException;
import ru.ganyaev.external_process.ExternalProcess;
import ru.ganyaev.file_system.BasicFileSystemManager;
import ru.ganyaev.file_system.FileSystemManager;
import ru.ganyaev.file_system.FileSystemManagerException;
import ru.ganyaev.vm_manager.Hipervisor;
import ru.ganyaev.vm_manager.VirtualMachine;
import ru.ganyaev.vmrun.PowerOptions;
import ru.ganyaev.vmrun.UserInterfaces;
import ru.ganyaev.vmrun.VmRunCommands;

public class WorkstationHipervisor extends Hipervisor {
    private static final Logger LOGGER = LogManager.getRootLogger();
    
    private Path tempFolderPath;
    private VmRunCommands vmRunCommands;

    private ExternalProcess externalProcess = new BasicExternalProcess();
    private FileSystemManager fileSystemManager = new BasicFileSystemManager();

    public WorkstationHipervisor(Path vmRunPath, Path tempFolderPath) {
        this.tempFolderPath = tempFolderPath;
        this.vmRunCommands = new VmRunCommands(vmRunPath);
    }

    public WorkstationHipervisor(Path vmRunPath, Path tempFolderPath,
            ExternalProcess externalProcess, FileSystemManager fileSystemManager) {
        this(vmRunPath, tempFolderPath);
        this.externalProcess = externalProcess;
        this.fileSystemManager = fileSystemManager;
    }

    private WorkstationVirtualMachine castToWorkstationVirtualMachine(
            VirtualMachine virtualMachine)
            throws WorkstationHipervisorException {
        WorkstationVirtualMachine workstaionVm = null;
        try {
            workstaionVm = (WorkstationVirtualMachine) virtualMachine;
        } catch (ClassCastException e) {
            throw new WorkstationHipervisorException(
                    "Virtual machine is not workstation.", e);
        }

        return workstaionVm;
    }

    @Override
    public void startVm(VirtualMachine virtualMachine)
            throws WorkstationHipervisorException {
        WorkstationVirtualMachine vm = castToWorkstationVirtualMachine(virtualMachine);
        Command startVmCommand = vmRunCommands.getStartCommand(vm.getVmxPath(),
                UserInterfaces.GUI);
        LOGGER.debug("Try to execute start vm command: " + startVmCommand);
        try {
            externalProcess.executeAndCheckExitCode(startVmCommand);
        } catch (ExecuteExternallProcessException e) {
            throw new WorkstationHipervisorException(
                    "Can not execute start vm process.", e);
        }
        Command listProcesesCommand = vmRunCommands
                .getListProcessesInGuestCommand(vm.getVmxPath(),
                        vm.getGuestUser(), vm.getGuestPassword());
        LOGGER.debug("Try to execute list processes command: "
                + listProcesesCommand);
        try {
            externalProcess.executeAndCheckExitCode(listProcesesCommand);
        } catch (ExecuteExternallProcessException e) {
            throw new WorkstationHipervisorException(
                    "Can not execute list proceses command.", e);
        }
    }

    @Override
    public void stopVm(VirtualMachine virtualMachine)
            throws WorkstationHipervisorException {
        WorkstationVirtualMachine vm = castToWorkstationVirtualMachine(virtualMachine);

        Command stopVmCommand = vmRunCommands.getStopCommand(vm.getVmxPath(),
                PowerOptions.HARD);
        LOGGER.debug("Try to execute stop vm command: " + stopVmCommand);
        try {
            externalProcess.executeAndCheckExitCode(stopVmCommand);
        } catch (ExecuteExternallProcessException e) {
            throw new WorkstationHipervisorException(
                    "Can not execute or analize stop vm command.", e);
        }
    }

    @Override
    public void resetVm(VirtualMachine virtualMachine)
            throws WorkstationHipervisorException {
        WorkstationVirtualMachine vm = castToWorkstationVirtualMachine(virtualMachine);

        Command resetVmCommand = vmRunCommands.getResetCommand(vm.getVmxPath(),
                PowerOptions.HARD);
        LOGGER.debug("Try to execute reset vm command: " + resetVmCommand);
        try {
            externalProcess.executeAndCheckExitCode(resetVmCommand);
        } catch (ExecuteExternallProcessException e) {
            throw new WorkstationHipervisorException(
                    "Can not execute of analize reset vm command.", e);
        }

        Command listProcesesCommand = vmRunCommands
                .getListProcessesInGuestCommand(vm.getVmxPath(),
                        vm.getGuestUser(), vm.getGuestPassword());
        LOGGER.debug("Try to execute list processes command: "
                + listProcesesCommand);
        try {
            externalProcess.executeAndCheckExitCode(listProcesesCommand);
        } catch (ExecuteExternallProcessException e) {
            throw new WorkstationHipervisorException(
                    "Can not execute list proceses command.", e);
        }
    }

    public WorkstationVirtualMachine createVirtualMachine(String name,
            String vmxPath, String guestUser, String guestPassword)
            throws WorkstationHipervisorException {
        Path vmTempFolderPath = tempFolderPath.resolve(name);
        LOGGER.debug("Try to create temp folder: " + vmTempFolderPath);
        try {
            fileSystemManager.createFolder(tempFolderPath);
            fileSystemManager.createFolder(vmTempFolderPath);
        } catch (FileSystemManagerException e) {
            throw new WorkstationHipervisorException(
                    "Can not create temp folders.", e);
        }

        return new WorkstationVirtualMachine(name, vmRunCommands, vmxPath,
                guestUser, guestPassword, vmTempFolderPath, externalProcess,
                fileSystemManager);
    }
}
